package PLCSymbolAndScope;

import PLCSymbolAndScope.PLCScope.PLCScope;
import PLCSymbolAndScope.PLCSymbolTables.PLCSymbolTable;
import PLCSymbolAndScope.PLCSymbolTables.PLCTotalSymbolTable;
import PLCSymbolAndScope.PLCSymbols.*;
import antlr4.PLCSTPARSERParser;

import java.util.Iterator;
import java.util.Stack;

//静态类——作用域栈
public final class PLCScopeStack {
    //全局作用域,在创建实例后初始化，下面相同
    static public PLCScope globalScope;

    //全局的符号表
    static public PLCSymbolTable globalSymbolTable;

    //等价于this->top(),当前作用域的引用，本身没有实体，初始为全局作用域,在创建实例后初始化，下面相同
    static public PLCScope currentScope;

    //初始的符号表的引用,本身没有实体，默认为全局的符号表
    static public PLCSymbolTable currentSymbolTable;

    //作用域栈
    static final private Stack<PLCScope> scopeStack = new Stack<>();

    //初始化全局变量
    static public void stackInit(){
        //全局符号
        PLCISTypeDeclType globalSymbol = new PLCISTypeDeclType();
        //初始化global符号表
        globalSymbolTable = new PLCSymbolTable();
        //初始化global作用域
        globalScope = new PLCScope();
        //压栈&设置引用关系
        push(globalSymbol, globalSymbolTable, globalScope);
    }

    //************************************************作用域压栈过程******************************************************
    /*

    例：如果遇到class声明，创建class符号，调用push方法将相应的作用域压栈，push方法中会调用setReference方法来设置引用关系
        push方法中会将class符号加入符号表

     */
    //*****************************************************************************************************************

    //设置符号、符号表、作用域之间的引用关系
    static private void setReference(PLCISTypeDeclType symbol, PLCSymbolTable table, PLCScope scope){
        scope.setDeclSymbol(symbol);
        scope.setScopeSymbolTable(table);
        table.setSrcSymbol(symbol);
        table.setTableScope(scope);
        symbol.setImportSymbolTable(table);
        symbol.setImportScope(scope);
    }

    //根据符号更新栈的内容,作用域和符号表在函数内创建,并设置引用关系,修改当前作用域, 并加入总表
    static public void push(PLCISTypeDeclType symbol){
        //作用域和符号表在函数内创建,并设置引用关系
        PLCScope newScope = new PLCScope();
        PLCSymbolTable newTable = new PLCSymbolTable();
        setReference(symbol, newTable, newScope);
        //将符添加到栈顶作用域符号表中
        symbol.localScope.getScopeSymbolTable().addSymbol(symbol);
        scopeStack.push(newScope);
        //修改当前作用域
        currentScope = newScope;
        currentSymbolTable = newTable;
        //加入总表
        PLCTotalSymbolTable.addMember(symbol);
    }

    //使用指定的符号 作用域和符号表向栈内添加新的符号, 并加入总表
    static public void push(PLCISTypeDeclType symbol, PLCSymbolTable table, PLCScope scope){
        //设置引用关系
        setReference(symbol, table, scope);
        scopeStack.push(scope);
        //修改当前作用域
        currentScope = scope;
        currentSymbolTable = table;
        //加入总表
        PLCTotalSymbolTable.addMember(symbol);
    }

    //弹出栈顶元素 并修改currentScope
    static public PLCScope pop(){
        if(currentScope == globalScope){
            //TODO:throw error
            System.out.println("弹出了全局栈");
            System.exit(-1);
        }
        PLCScope tempScope = scopeStack.pop();
        //修改currentScope
        currentScope = scopeStack.peek();
        currentSymbolTable = currentScope.getScopeSymbolTable();
        return tempScope;
    }

    //返回栈顶元素
    static public PLCScope top(){
        return scopeStack.peek();
    }

    //*******************************************************静态语义检查方法**********************************************

    static public PLCSymbol usingNamespaceSemanticCheck(Iterator<PLCSTPARSERParser.Namespace_nameContext> iterator){

        //检查使用命名空间是否正确
        PLCSymbol namespaceSymbol = new PLCNamespaceDeclSymbol();

        //查找头命名空间
        if(PLCScopeStack.currentSymbolTable.findSymbol(iterator.next().getText())
                instanceof PLCNamespaceDeclSymbol){
            namespaceSymbol = PLCScopeStack.currentSymbolTable.findSymbol(iterator.next().getText());

        }else{

            System.out.println("The "+iterator.next().getText()+"Namespace is Null");
            return null;

        }
        //在当前头作用域下查找嵌套命名空间符号是否存在
        while (iterator.hasNext()){
            namespaceSymbol = ((PLCNamespaceDeclSymbol) namespaceSymbol).importScope.findSymbol(iterator.next().getText());
            if(!(namespaceSymbol instanceof PLCNamespaceDeclSymbol)){
                System.out.println("Can't find this Namespace");
                return null;
            }
        }
        return namespaceSymbol;
    }

    //检查符号是否是重定义或未定义使用 TODO:待写
    static public void symbolSemanticCheck(){

    }
}
